/*********************************************************************\
*                                                                     *
*         Jamper - Java XML mapper (visual XSLT generator)            *
*                Copyright (C) 2005 Filip Pavlovic                    *
*                    sqba@users.sourceforge.net                       *
*                                                                     *
*  This program is free software; you can redistribute it and/or      *
*  modify it under the terms of the GNU General Public License        *
*  as published by the Free Software Foundation; either version 2     *
*  of the License, or (at your option) any later version.             *
*                                                                     *
*  This program is distributed in the hope that it will be useful,    *
*  but WITHOUT ANY WARRANTY; without even the implied warranty of     *
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the      *
*  GNU General Public License for more details.                       *
*                                                                     *
*  You should have received a copy of the GNU General Public License  *
*  along with this program; if not, write to the Free Software        *
*  Foundation, Inc., 59 Temple Place - Suite 330, Boston,             *
*  MA 02111-1307, USA.                                                *
*                                                                     *
\*********************************************************************/


package sqba.jamper.map.tree;


//import sqba.jamper.tree.model.XsomModel;
import sqba.jamper.map.ILinkable;
import sqba.jamper.map.ObjectProperty;
import sqba.jamper.map.object.DefaultLinkable;
//import sqba.jamper.map.object.DefaultMapObject;
import sqba.jamper.map.object.IMapObject;
import sqba.jamper.map.object.LinkLine;
import sqba.jamper.util.PathList;
import sqba.jamper.util.XMLMapperInit;
import sqba.jamper.util.XMLUtils;

import java.io.IOException;

//import java.util.Map;
//import java.util.HashMap;
//import java.util.Iterator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
//import java.util.Iterator;

import java.awt.Point;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;

import java.awt.event.ActionEvent;

import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;

//import javax.swing.JTree;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.AbstractAction;

import javax.swing.tree.TreePath;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.DefaultMutableTreeNode;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;


/**
 * This class represents an element in the XML tree.
 *
 * @author Filip Pavlovic
 * @version 1.0
 */
public class MapperTreeNode extends DefaultMutableTreeNode
implements Transferable, ILinkable
{
	public static final DataFlavor mapperTreeNodeFlavor =
					new DataFlavor(TreePath.class, "Node Info");
	public static final DataFlavor localMapperTreeNodeFlavor =
					new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType,
									"Local Node Info");

	DataFlavor	_flavors[] = { mapperTreeNodeFlavor, localMapperTreeNodeFlavor };

	private TreePath		_path		= null;
	private MapperTree		_tree		= null;
	private String			_name		= "Root";
	private ArrayList		_properties	= new ArrayList();
	private boolean			_bAttribute = false;
	private DefaultLinkable	_linkable	= new DefaultLinkable();
	private MapperTreeNode	_textNode	= null;
	private String			_value		= "";
	private MyMenu			_menu		= null;//new MyMenu();
	private boolean			_bTextNode	= false;
	private int				_iValueProp	= -1;
	private int				_iNameProp	= -1;
	private boolean			_bDontDisplay = false;


    class MyMenu extends JMenu
    {
		private MapperTreeNode _node = MapperTreeNode.this;

		public MyMenu()
    	{
    		super("Node");

			JMenuItem menuItem;

			if(!_node.isTextNode()) {
				menuItem = new JMenuItem("Rename...");
		        menuItem.addActionListener(new AbstractAction() {
			        public void actionPerformed(ActionEvent e) {
						String name = JOptionPane.showInputDialog("Enter new name", _name);
						setName(name);
			        }
			    });
				this.add(menuItem);

				menuItem = new JMenuItem("Set Value...");
		        menuItem.addActionListener(new AbstractAction() {
			        public void actionPerformed(ActionEvent e) {
			        	if(!_node.isTextNode()) {
							String v = _node.getValue();
							String text = "Please enter new " +
									(_node.isLeaf()?"attribute":"element") + 
									" value:";
							String value = JOptionPane.showInputDialog(text, v);
							setValue(value);
						}
			        }
			    });
				this.add(menuItem);
			}

			if(!_node.isLeaf()) {
				menuItem = new JMenuItem("Set root");
		        menuItem.addActionListener(new AbstractAction() {
			        public void actionPerformed(ActionEvent e) {
			        	((MapperTree)_tree).setRoot(_node);
			        }
			    });
				this.add(menuItem);

				menuItem = new JMenuItem("Add Child");
		        menuItem.addActionListener(new AbstractAction() {
			        public void actionPerformed(ActionEvent e) {
						MapperTreeNode newNode = new MapperTreeNode("newElement", _node.getTree());
						_node.add(newNode);
						_node.update();
			        }
			    });
				this.add(menuItem);

				menuItem = new JMenuItem("Add Attribute");
		        menuItem.addActionListener(new AbstractAction() {
			        public void actionPerformed(ActionEvent e) {
						MapperTreeNode newNode = new MapperTreeNode("newAttribute", _node.getTree(), true);
						_node.add(newNode);
						_node.update();
			        }
			    });
				this.add(menuItem);
			}

			if(!_node.isTextNode()) {
				menuItem = new JMenuItem("Remove");
		        menuItem.addActionListener(new AbstractAction() {
			        public void actionPerformed(ActionEvent e) {
			        	MapperTreeNode parent = (MapperTreeNode)_node.getParent();
			        	if(null != parent)
			        		parent.removeChild(_node);
			        }
			    });
				this.add(menuItem);

				menuItem = new JMenuItem("Show XPath...");
		        menuItem.addActionListener(new AbstractAction() {
			        public void actionPerformed(ActionEvent e) {
						JOptionPane.showMessageDialog(null,
								getXPath(),
								"XPath",
								JOptionPane.PLAIN_MESSAGE,
								XMLMapperInit.loadIcon(XMLMapperInit.ICON_APP));
			        }
			    });
				this.add(menuItem);
			}
		}
	}


	/**
	 * Constructor
	 * initializes the node, sets it's name and parent tree.
	 *
	 * @param name String, node name
	 * @param tree Parent tree
	 * @param bAttrib If true then an attribute node will be created.
	 */
	public MapperTreeNode(String name, MapperTree tree, boolean bAttrib)
	{
		_tree = tree;
		_name = name;

		if(null == tree)
			return;

		setAllowsChildren(!bAttrib);
		
		// Add text node
		if(!tree.isSinkTree() && !bAttrib) {
			_textNode = new MapperTreeNode(tree);
			this.add(_textNode);
		}
		
		_menu = new MyMenu();

		_iNameProp	= addProperty("Name", _name);
		_iValueProp	= addProperty("Value", _value);
	}
	
	/**
	 * Constructor, creates a element node.
	 *
	 * @param name String, node name
	 * @param tree Parent tree
	 */
	public MapperTreeNode(String name, MapperTree tree)
	{
		this(name, tree, false);
	}

	/**
	 * Constructor, creates a temporary 'Loading...' node.
	 *
	 * @param bDontDisplay boolean, true if the node will be hidden after loading
	 * @param name String, node name
	 * @param tree Parent tree
	 */
	public MapperTreeNode(boolean bDontDisplay, String name, MapperTree tree)
	{
		this(name, tree, false);
		_bDontDisplay = bDontDisplay;
	}

	/**
	 * Constructor, creates a text node.
	 *
	 * @param name String, node name
	 * @param tree Parent tree
	 */
	private MapperTreeNode(MapperTree tree)
	{
		_tree = tree;
		_bTextNode = true;
		_name = "text()";
		setAllowsChildren(false);
	}

	/**
	 * Sets node value.
	 *
	 * @param value New node value
	 */
	public void setValue(String value)
	{
		if(value!=null && value.length()>0)
		{
			if( !isTextNode() && null != _textNode )
				_textNode.setValue(value);
			else
				_value = value.trim();
			
			// Update value property
			if(_iValueProp >= 0)
				((ObjectProperty)_properties.get(_iValueProp)).setValue(value);
		}
	}

	/**
	 * Returns node value.
	 *
	 * @return String
	 */
	public String getValue()
	{
		if( !isTextNode() && null != _textNode )
			return _textNode.getValue();
		else
			return _value;
	}

	/**
	 * Sets node name.
	 *
	 * @param name
	 */
	public void setName(String name)
	{
		if(name!=null && name.length()>0)
		{
			_name = name;
		
			// Update value property
			if(_iNameProp >= 0)
				((ObjectProperty)_properties.get(_iNameProp)).setValue(name);
		}
	}

	/**
	 * Notifies the tree of an update so it can repaint.
	 */
	public void update()
	{
		DefaultTreeModel treeModel = (DefaultTreeModel)_tree.getModel();
		treeModel.nodeStructureChanged(this);
		((MapperTree)_tree).expandAll(_path, true);
	}

	private boolean getShowValue()
	{
		if(_tree != null)
			return ((MapperTree)_tree).getShowValues();
		else
			return false;
	}

	/**
	 * Returns node name.
	 *
	 * @return Node name string and, optionaly, node value.
	 */
	public String toString()
	{
		if(getShowValue()) {
			if(_bAttribute)
				return _name + "=\"" + _value + "\"";
			else
				return _name + " (" + _value + ")";
		}

		return _name;
	}

	/**
	 * Returns node name usable in creating the XPath.
	 *
	 * @return Node name string.
	 */
	public String getXPathName()
	{
		int index = getIndex();
		String result = ((_bAttribute && !isTextNode()) ? "@" : "") + _name;
		if(index > 1 && hasSiblings() && !_bAttribute)
			result += "[" + index + "]";

		return result;
	}

	public boolean isTextNode() { return _bTextNode; }
	
	/**
	 * Checks if the node has child elements.
	 */
	public boolean isEmptyNode()
	{
		Enumeration e = this.children();
		while ( e.hasMoreElements() ) {
			MapperTreeNode child = (MapperTreeNode)e.nextElement();
			if(!child.isLeaf())
				return false;
		}
		return true;
	}

	/**
	 * If parent node has more than one child node with the same name,
	 * this function returns this node's index in it's parent's child
	 * nodes with the same name. Otherwise returns 1.
	 */
	private int getIndex()
	{
		int index = 1;
		MapperTreeNode parent = (MapperTreeNode)this.getParent();
		if(null != parent) {
			Enumeration e = parent.children();
			while ( e.hasMoreElements() ) {
				MapperTreeNode child = (MapperTreeNode)e.nextElement();
				if(this == child)
					break;
				else if(child.getName().equals(this._name))
					index++;
			}
		}
		return index;
	}

	private boolean hasSiblings()
	{
	  MapperTreeNode parent = (MapperTreeNode)this.getParent();
	  if(null != parent) {
		  Enumeration e = parent.children();
		  int index = 0;
		  while ( e.hasMoreElements() && index < 2 ) {
		    index++;
		  }
		  return (index > 1);
	  }
	  return false;
	}

	/**
	 * Sets TreePath of this node.
	 *
	 * @param path This node's TreePath
	 */
	public void setTreePath(TreePath path)	{ _path = path; }

	/**
	 * Returns this node's TreePathh.
	 *
	 * @return This node's TreePath
	 */
	public TreePath	getTreePath() { return _path; }

	/**
	 * Sets this node's parent tree.
	 *
	 * @param tree This node's parent tree
	 */
	public void setTree(MapperTree tree)
	{
		_tree = tree;
		// traverse children
		for (Enumeration e = children() ; e.hasMoreElements() ;) {
			MapperTreeNode child = (MapperTreeNode)e.nextElement();
			child.setTree(tree);
		}

	}

	/**
	 * Returns this node's parent tree.
	 *
	 * @return This node's parent tree
	 */
	public MapperTree	getTree()				{ return _tree; }

	/////////////////////////////////////////////////////////////
	// DefaultMutableTreeNode overrides
	/////////////////////////////////////////////////////////////
	/**
	 * Returns true if this node cannot have children
	 * (node is an attribute).
	 *
	 * @return true if node is an attribute
	 */
	public boolean isLeaf()						{ return _bAttribute; }

	/**
	 * Sets this node to be an attribute or a node.
	 *
	 * @param attrib boolean, if true then the element
	 * is a node, else, the node is an attribute
	 */
	public void setAllowsChildren(boolean allows)
	{
		super.setAllowsChildren(allows);
		_bAttribute = !allows;

		//if(_textNode!=null && (!_name.equals("text()")) && !allows)
		//	this.remove(_textNode);
		//	_textNode = null;
	}

	/////////////////////////////////////////////////////////////
	// Transferable interface implementation
	/////////////////////////////////////////////////////////////
	public synchronized DataFlavor[] getTransferDataFlavors()
		{ return _flavors; }

	public boolean isDataFlavorSupported(DataFlavor flavor)
	{
		//return (flavor.getRepresentationClass() == TreePath.class);
		return java.util.Arrays.asList(_flavors).contains(flavor);
	}

	public synchronized Object getTransferData(DataFlavor flavor)
	throws UnsupportedFlavorException, IOException
	{
		if (isDataFlavorSupported(flavor)) {
			if (flavor.equals(mapperTreeNodeFlavor))
				return (Object)this;
			else if (flavor.equals(localMapperTreeNodeFlavor))
				return this;
			else
				throw new UnsupportedFlavorException(flavor);
		} else {
			throw new UnsupportedFlavorException(flavor);
		}
	}

	/**
	 * Returns child node with the given name.
	 *
	 * @param name child name
	 *
	 * @return MapperTreeNode or null if name not found
	 */
	public MapperTreeNode getChildByName(String name)
	{
		Enumeration e = this.children();
		while ( e.hasMoreElements() ) {
			MapperTreeNode child = (MapperTreeNode)e.nextElement();
			//String s = child.getXPathName();
			if(child.getXPathName().equals(name))
				return child;
		}

		return null;
	}

	//////////////////////////////////////////////////////////////////////
	// MapObject overrides
	//////////////////////////////////////////////////////////////////////
	public boolean isSelected()	{ return _linkable.isSelected(); }

	public void select(boolean sel)
	{
		_linkable.select(sel);
		if(sel) {
			MapperTree tree = (MapperTree)_tree;
			TreePath path = tree.getFirstVisibleAncestor(_path);
			tree.scrollPathToVisible(path);
			tree.setSelectionPath(path);
			//System.out.println(getValue());
		}
		_tree.repaint();
	}

	public void draw( Graphics g, JComponent parent )
	{
		MapperTree tree = (MapperTree)_tree;

		TreePath path = tree.getFirstVisibleAncestor(_path);
		Rectangle rect = tree.getPathBounds(path);
		
		if(null == rect)
			return;

		int x1 = rect.x + rect.width;
		int y1 = rect.y+rect.height/2;//-tree.getScrollPos();
		int x2 = tree.getWidth();
		int y2 = y1;

		if(tree.isSinkTree()) {
			x1 = 0;
			x2 = rect.x;
		}

		Color color = g.getColor();
		Color newColor = (isSelected() ? Color.RED : Color.BLACK);
		g.setColor(_path.equals(path) ? newColor : Color.LIGHT_GRAY);
		g.drawLine(x1, y1, x2, y2);
		g.setColor(color);
	}

	public boolean hitTest(Point pt)
	{
		Rectangle rect = _tree.getPathBounds(_path);
		return rect.contains(pt);
	}

	//public String getName()				{ return "MapperTreeNode"; }
	public String getName()					{ return _name; }
	public IMapObject getParentObject()		{ return _linkable.getParentObject(); }

	public Collection getChildObjects()		{ return _linkable.getChildObjects(); }

	public IMapObject getChildAt(Point pt)	{ return this; }

	//public JMenu getMenu()					{ return _menu; }
	public JMenu getMenu() 					{ return _menu; }
//	public void setMenu(JMenu menu)	{}

	public void removeChild(IMapObject obj)
	{
		if(obj instanceof MapperTreeNode) {
			MapperTreeNode node = (MapperTreeNode)obj;
    		if(!node.isTextNode()) {
				DefaultTreeModel treeModel = (DefaultTreeModel)_tree.getModel();
				treeModel.removeNodeFromParent(node);
				update();
			}
		}
	}

	//public String getXML()					{ return ""; }

	//////////////////////////////////////////////////////////////////////
	// Linkable interface implementation
	//////////////////////////////////////////////////////////////////////
	public boolean		isLinked()			{ return _linkable.isLinked(); }
	public Collection	getLinks()			{ return _linkable.getLinks(); }

	//public void			removeLinks()		{ _linkable.removeLinks(); }

	public Point getOrigin()
	{
		MapperTree tree = (MapperTree)_tree;

		TreePath path = tree.getFirstVisibleAncestor(_path);
		Rectangle rect = tree.getPathBounds(path);
		
		Point pt = new Point(0, 0);
		if(!tree.isSinkTree())
			pt.x = tree.getWidth();
		if(null != rect)
			pt.y = rect.y+rect.height/2-tree.getScrollPos();

		return pt;
	}

	public boolean 	isOutput()
	{
		if(_tree != null)
			return ((MapperTree)_tree).isSinkTree();
		else
			return false;
	}

	public String getFullPath()
	{
		String path = "";
		MapperTreeNode parent = (MapperTreeNode)this.getParent();
		
		//while(parent != null && !parent.isRoot()) {
		while(parent != null) {
			// Skip the 'loading...' node
			if(!_bDontDisplay)
			{
				//path = parent.toString() + "/" + path ;
				path = parent.getXPathName() + "/" + path ;
			}
			parent = (MapperTreeNode)parent.getParent();
		}
		
		//if(_bAttribute && !_name.equals("text()"))
		//	path += "@";
		//path += this.toString();
		
		path += this.getXPathName();
		return "/" + path;
	}
	
	public String		getXPath()			{ return getFullPath(); }

	public ILinkable 	getSource()	{ return _linkable.getSource(); }

	public boolean willAccept(ILinkable src)
	{
		if((src != null) && (src instanceof MapperTreeNode)) {
			MapperTreeNode node = (MapperTreeNode)src;
			if(node.getTree().equals(_tree))
				return false;
		}
		return (!(isLinked() && ((MapperTree)_tree).isSinkTree()));
	}

//	public String getTypeName() { return "MapperTreeNode"; }

	public IMapObject getChild(String name)
	{
		Enumeration e = this.children();
		while ( e.hasMoreElements() ) {
			MapperTreeNode child = (MapperTreeNode)e.nextElement();
			if(name.equals(child.getName()))
				return child;
		}
		return null;
	}
	
	public String getDisplayName() { return getName(); }

	public int getID()			{ return hashCode(); } 
	public void setID(int ID)	{ } 
	
	public String getUniqueName() { return getName(); }

	//////////////////////////////////////////////////////////////////////
	// PropObject interface implementation
	//////////////////////////////////////////////////////////////////////
	public int getPropertyCount()			{ return _properties.size(); }
	public int addProperty(String name, Object value)
	{
		ObjectProperty prop = new ObjectProperty(this, name, value);
		if(_properties.add(prop))
			return _properties.indexOf(prop);
		else
			return -1;
	}
	public void removeProperty(int index)	{ _properties.remove(index); }
	public void removeProperty(ObjectProperty prop)
		{ _properties.remove(prop); }
	public ObjectProperty getProperty(int index)
		{ return (ObjectProperty)_properties.get(index); }
	//public ObjectProperty getProperty(String name);
	public void propertyUpdated(ObjectProperty prop)
		{ System.out.println("propertyUpdated(" + prop + ")"); }



	public boolean hasToBeRendered()
	{
		Enumeration e = this.children();
		while ( e.hasMoreElements() ) {
			MapperTreeNode child = (MapperTreeNode)e.nextElement();
			if(child.hasToBeRendered())
				return true;
		}
		return isLinked() || (this.getValue().trim() != "");
	}

	/**
	 * If the parent tree is sink (output) renders output
	 * element, otherwise renders linked source object.
	 *
	 * @param context current context (XPath)
	 * @param parent parent element, context
	 * @return new context node or parent if context has not changed.
	 */
	public Element render(PathList context, Element parent)
	{
		if(_tree.isSinkTree()) {
			return renderOutput(context, parent);
		} else {
			Iterator iter = getLinks().iterator();
			while( iter.hasNext() ) {
				LinkLine line = (LinkLine)iter.next();
				if(line.getSrc() == this) {
					Element newElem = line.render(context, parent);
					if(null != newElem)
						context.set(getFullPath());
					return newElem;
				}
			}
			return null;
		}
	}
	
	/**
	 * Renders output element.
	 *
	 * @param context current context (XPath)
	 * @param parent parent element, context
	 * @return new context node or parent if context has not changed.
	 */
	private Element renderOutput(PathList context, Element parent)
	{
		if(null == parent)	
			return null;
		
		if(!hasToBeRendered())
			return parent;

		Document	doc			= parent.getOwnerDocument();
		String		prevContext	= context.toString();
		Element		result		= null;
		Element		newElement	= createOutputElement(doc);
		
		if(isLinked()) {
			ILinkable src = getSource();
			if(src instanceof MapperTreeNode) {
				MapperTreeNode node = (MapperTreeNode)src;
				if(!node.isLeaf())
					result = node.render(context, parent);
			}
		}

		if(null != result){
			result.appendChild(newElement);
			NamedNodeMap t = newElement.getAttributes();
			Node tmp;
			for( int i = 0 ; i < t.getLength() ; i++ ){
				tmp = t.item(i);
				result.setAttribute( tmp.getNodeName(), tmp.getNodeValue() );
			}
			
		}
		else
			result = newElement;
		
		if(parent != result)
			parent.appendChild(result);

		renderOutputContents(context, newElement);

		for (Enumeration e=children();e.hasMoreElements();) {
			MapperTreeNode child = (MapperTreeNode)e.nextElement();
			child.render(context, newElement);
		}
	
		context.set(prevContext);
		
		return result;
	}

	/**
	 * Creates new output element.
	 *
	 * @param doc DOM document
	 * @return new element.
	 */
	private Element createOutputElement(Document doc)
	{
		String parentName = getName();
		Element newElement = null;
		if(isLeaf()) {
			newElement = doc.createElement("xsl:attribute");
			newElement.setAttribute("name", parentName);
		} else {
			newElement = doc.createElement(parentName);
		}
		return newElement;
	}

	/**
	 * Renders contents of the output element.
	 *
	 * @param context current context (XPath)
	 * @param parent parent element, context
	 */
	private void renderOutputContents(PathList context, Element parent)
	{
		if(isLinked()) {
			ILinkable src = getSource();
			if(src instanceof MapperTreeNode) {
				MapperTreeNode node = (MapperTreeNode)src;
				String srcPath = context.getRelativePath(node.getFullPath()); // Take in account parent xsl:for-each!
				if(node.isLeaf() && null!=parent) {
					Document doc = parent.getOwnerDocument();
					Element tmp = doc.createElement("xsl:value-of");
					tmp.setAttribute("select", srcPath);
					parent.appendChild(tmp);
				}
			} else if(src.getParentObject() != null) {
				IMapObject parentObj = src.getParentObject();
				parentObj.render(context, parent);
			}
		} else {
			String val = getValue();
			if(val.length() > 0)
				parent.setTextContent(XMLUtils.escape(val));
		}
	}

	public void setParent(MapperTreeNode node)	{ this.parent = node; }
	public Element save(Element node)			{ return null; }
	public boolean load(Element node)			{ return false; }
	public void move(int x, int y)				{}
	public Element renderDeclaration(PathList context, Element parent) { return null; }
	public void renderDeclarations(PathList context, Element parent) {}
	
//	public boolean rendersComplexNode()		{ return false; }
	public Element renderDeclaration(PathList context, Document doc) { return null; }
//	public boolean requiresEmbededDeclaration() { return false; }

	
	
	

	/*public boolean willAccept(Linkable src)
	{
		if(src instanceof MapperTreeNode) {
			MapperTreeNode node = (MapperTreeNode)src;
			if(node._tree.equals(_tree))
				return false;
		}
		return _linkable.willAccept(src);
	}*/

	/*public void drawLinks(Graphics g, int width)
	{
		//if(((MapperTree)_tree).isSinkTree())
		//	return;
		Iterator iter = getLinks().iterator();
		while ( iter.hasNext() ) {
    		LinkLine line = (LinkLine)iter.next();
			Linkable src = line.getSrc();
			Linkable dst = line.getDest();
			//if((dst != null) && (!dst.equals(this))) {
			// This function had to be overridden
			// because of the >>this<< pointer!
			if(src.equals(this)) {
				line.draw(g, width);
				//dst.drawLinks(g, width);
			}
    	}
	}*/




	/**
	 * This class represents a generic node property.
	 *
	 * @author Filip Pavlovic
	 * @version 1.0
	 */
	/*public class NodeProperty
	{
		public String name  = "";
		public String value = "";

		public NodeProperty(String name, String value)
		{
			this.name  = name;
			this.value = value;
		}

		public String toString()
			{ return this.name + " = '" + this.value + "'"; }
	}*/

	//public Map getProperties() { return _properties; }

	/**
	 * Returns the number of this node's properties.
	 *
	 * @return int, number of this node's properties
	 */
//	public int getPropertyCount()
//		{ return _properties.size(); }

	/**
	 * Adds new property to this node's properties.
	 *
	 * @param name String, property name.
	 * @param value String, property value.
	 */
//	public void addProperty(String name, String value)
//		{ _properties.add(new NodeProperty(name, value)); }

	/**
	 * Returns the name of the property with given index.
	 *
	 * @param index int, property index.
	 *
	 * @return String, property name
	 */
/*	public String getPropertyName(int index)
	{
		if(!_properties.isEmpty())
			return ((NodeProperty)_properties.get(index)).name;
		return "";
	}
*/
	/**
	 * Returns the value of the property with given index.
	 *
	 * @param index int, property index.
	 *
	 * @return String, property value
	 */
/*	public String getPropertyValue(int index)
	{
		if(!_properties.isEmpty())
			return ((NodeProperty)_properties.get(index)).value;
		return "";
	}
*/
	public IMapObject copy() { return null; }
}
